import json
import logging
import pika
from tornado import ioloop, web, options
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
from functools import partial

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class RabbitMQProducer:
    """RabbitMQ 生产者封装类"""
    
    def __init__(self, host='localhost', port=5672, username='guest', password='guest'):
        self.connection_params = pika.ConnectionParameters(
            host=host,
            port=port,
            credentials=pika.PlainCredentials(username, password),
            heartbeat=600,
            blocked_connection_timeout=300
        )
        self.connection = None
        self.channel = None
        self.queue_name = 'api_requests'
        
    def connect(self):
        """建立 RabbitMQ 连接"""
        try:
            self.connection = pika.BlockingConnection(self.connection_params)
            self.channel = self.connection.channel()
            # 声明一个持久化队列
            self.channel.queue_declare(queue=self.queue_name, durable=True)
            logger.info("Connected to RabbitMQ successfully")
        except Exception as e:
            logger.error(f"Failed to connect to RabbitMQ: {e}")
            raise
            
    def publish_message(self, message):
        """发布消息到队列"""
        if not self.connection or self.connection.is_closed:
            self.connect()
            
        try:
            self.channel.basic_publish(
                exchange='',
                routing_key=self.queue_name,
                body=json.dumps(message),
                properties=pika.BasicProperties(
                    delivery_mode=2,  # 使消息持久化
                )
            )
            logger.debug(f"Message published to RabbitMQ: {message}")
            return True
        except Exception as e:
            logger.error(f"Failed to publish message: {e}")
            # 尝试重新连接并发送
            try:
                self.connect()
                self.channel.basic_publish(
                    exchange='',
                    routing_key=self.queue_name,
                    body=json.dumps(message),
                    properties=pika.BasicProperties(
                        delivery_mode=2,
                    )
                )
                return True
            except Exception as retry_e:
                logger.error(f"Retry failed: {retry_e}")
                return False

class RequestHandler(web.RequestHandler):
    """处理客户端请求的 Tornado 处理器"""
    
    executor = ThreadPoolExecutor(max_workers=10)  # 线程池执行阻塞操作
    rabbitmq_producer = RabbitMQProducer(
        host='localhost',
        port=5672,
        username='guest',
        password='guest'
    )
    
    async def post(self):
        """处理 POST 请求"""
        try:
            # 解析请求数据
            data = json.loads(self.request.body)
            logger.info(f"Received request: {data}")
            
            # 验证必要字段
            if not data.get('message'):
                raise web.HTTPError(400, reason="Missing 'message' field")
                
            # 异步将消息写入 RabbitMQ
            result = await self.write_to_rabbitmq(data)
            
            if result:
                self.write({
                    "status": "success",
                    "message": "Request has been queued"
                })
            else:
                raise web.HTTPError(500, reason="Failed to queue request")
                
        except json.JSONDecodeError:
            raise web.HTTPError(400, reason="Invalid JSON format")
        except Exception as e:
            logger.error(f"Error processing request: {e}")
            raise web.HTTPError(500, reason=str(e))
    
    @run_on_executor
    def write_to_rabbitmq(self, message):
        """将消息写入 RabbitMQ (在线程池中执行)"""
        return self.rabbitmq_producer.publish_message(message)

def make_app():
    """创建 Tornado 应用"""
    return web.Application([
        (r"/task", RequestHandler),
    ], compress_response=True)

if __name__ == "__main__":
    # 解析命令行参数
    options.define("port", default=8889, help="Run on the given port", type=int)
    options.parse_command_line()
    
    # 创建并启动应用
    app = make_app()
    app.listen(options.options.port)
    logger.info(f"Server started on port {options.options.port}")
        
    try:
        # 初始化 RabbitMQ 连接
        RequestHandler.rabbitmq_producer.connect()
        
        # 启动 IOLoop
        ioloop.IOLoop.current().start()
    except KeyboardInterrupt:
        logger.info("Server stopped")
    except Exception as e:
        logger.error(f"Server error: {e}")